/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.editor; import javax.swing.text.BadLocationException; import javax.swing.text.Document; /** * Block of text created using two marks. These blocks can be chained. * Existing block can be compared to other block (better pair of positions) * and there's extensive list of result values of such comparison. * * @author Miloslav Metelka * @version 1.00 */ public class MarkBlock { /** This value is not used directly in this class * but can be used by other classes to report that * the comparison of blocks has no sense for some reason. */ public static final int INVALID = 0; /** Single bit value that means that tested block * and THIS block partially or fully overlap. If this bit is not * set then the blocks don't overlap at all. The values for which * this bit is set are: OVERLAP_BEGIN, OVERLAP_END, EXTEND_BEGIN, * EXTEND_END, INCLUDE, INSIDE_BEGIN, INSIDE_END, SAME, INNER. * The values for which this bit is not set are: BEFORE, AFTER, * CONTINUE_BEGIN, CONTINUE_END. */ public static final int OVERLAP = 1; /** Single bit value that means that the tested block doesn't * overlap with THIS block, but either its start position * is equal to end position of THIS block or its end position * is equal to the start position of THIS block. Simply they together * make one continuous block. The values for which this bit is set * are: CONTINUE_BEGIN, CONTINUE_END. */ public static final int CONTINUE = 2; /** Single bit value meaning that the tested block has zero size. */ public static final int EMPTY = 4; /** Single bit value meaning that THIS block has zero size. */ public static final int THIS_EMPTY = 8; /** Two bit value meaning that the tested block fully * includes THIS block. The block must be extended at least by * one character, otherwise the 'inside' values will be used. * It is included in the following * values: EXTEND_BEGIN, INCLUDE, EXTEND_END. * The value includes OVERLAP. */ public static final int EXTEND = 16 | OVERLAP; /** Two bit value meaning that the tested block is fully * inside THIS block. It is included in the following * values: INSIDE_BEGIN, SAME, INSIDE_END. * The value includes OVERLAP. */ public static final int INSIDE = 32 | OVERLAP; /** Tested block completely before THIS mark block. */ public static final int BEFORE = 64; /** Tested block completely after THIS mark block. */ public static final int AFTER = 128; /** Tested block completely before THIS mark block but its * end position equals to the start position of THIS block. * They together make one continuous block. * The value is BEFORE | CONTINUE. */ public static final int CONTINUE_BEGIN = BEFORE | CONTINUE; /** Tested block completely after THIS mark block but its * start position equals to the end position of THIS block. * They together make one continuous block. * The value is AFTER | CONTINUE. */ public static final int CONTINUE_END = AFTER | CONTINUE; /** Tested block partly covers begining of THIS mark block. * The value includes OVERLAP. */ public static final int OVERLAP_BEGIN = 256 | OVERLAP; /** Tested block partly covers end of THIS mark block. * The value includes OVERLAP. */ public static final int OVERLAP_END = 512 | OVERLAP; /** Start position of the tested block is lower than * the start position of THIS block and both end positions * are the same. * The value is OVERLAP_BEGIN | EXTEND. */ public static final int EXTEND_BEGIN = OVERLAP_BEGIN | EXTEND; /** End position of the tested block is greater than * the end position of THIS block and both start positions * are the same. * The value is OVERLAP_END | EXTEND. */ public static final int EXTEND_END = OVERLAP_END | EXTEND; /** Tested block fully includes THIS block and extends it * by at least one character in both directions. * The value is EXTEND_BEGIN | EXTEND_END. */ public static final int INCLUDE = EXTEND_BEGIN | EXTEND_END; /** Tested block completely inside THIS block and its end * position is lower than end position of THIS block * and start positions are the same. * The value includes INSIDE. */ public static final int INSIDE_BEGIN = 1024 | INSIDE; /** Tested block completely inside THIS block and its start * position is greater than THIS block start position and * end positions are the same. * The value includes INSIDE. */ public static final int INSIDE_END = 2048 | INSIDE; /** Tested block is fully inside THIS block and there * is at least one more character left in THIS block * after the end of the tested block in both directions. * The value includes INSIDE. */ public static final int INNER = 4096 | INSIDE; /** The blocks have exactly the same start and end positions. * They simply cover exactly the same area. * The value is INSIDE_BEGIN | INSIDE_END. */ public static final int SAME = INSIDE_BEGIN | INSIDE_END; /** This value can be used to clear the two bits saying * if the tested or THIS block are empty (The EMPTY and THIS_EMPTY are cleared). * To do that, use value ANDed by IGNORE_EMPTY expression. */ public static final int IGNORE_EMPTY = ~(EMPTY | THIS_EMPTY); /** Next block in the chain */ protected MarkBlock next; /** Previous block in the chain */ protected MarkBlock prev; public Mark startMark; public Mark endMark; protected BaseDocument doc; public MarkBlock(BaseDocument doc, Mark startMark, Mark endMark) { this.doc = doc; this.startMark = startMark; this.endMark = endMark; } /** Construct block with given marks */ public MarkBlock(BaseDocument doc, int startPos, int endPos) throws BadLocationException { this(doc, new Mark(), new Mark(), startPos, endPos); } /** Construct block from positions on some document */ public MarkBlock(BaseDocument doc, Mark startMark, Mark endMark, int startPos, int endPos) throws BadLocationException { this(doc, startMark, endMark); try { doc.op.insertMark(startMark, startPos); try { doc.op.insertMark(endMark, endPos); } catch (BadLocationException e) { try { startMark.remove(); } catch (InvalidMarkException e2) { if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N e2.printStackTrace(); } } throw e; } catch (InvalidMarkException e) { if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N e.printStackTrace(); } } } catch (InvalidMarkException e) { if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N e.printStackTrace(); } } } /** Insert block before this one * @return inserted block */ public MarkBlock insertChain(MarkBlock blk) { MarkBlock thisPrev = this.prev; blk.prev = thisPrev; blk.next = this; if (thisPrev != null) { thisPrev.next = blk; } this.prev = blk; return blk; } /** Add block after this one * @return added block */ public MarkBlock addChain(MarkBlock blk) { if (next != null) { next.insertChain(blk); } else { setNextChain(blk); } return blk; } /** Remove this block from the chain * @return next chain member or null for end of chain */ public MarkBlock removeChain() { MarkBlock thisNext = this.next; MarkBlock thisPrev = this.prev; if (thisPrev != null) { // not the first thisPrev.next = thisNext; this.prev = null; } if (thisNext != null) { thisNext.prev = thisPrev; this.next = null; } destroyMarks(); return thisNext; } /** Compares the position of the given block against current block. * @param startPos starting position of the compared block * @param endPos ending position of the compared block or it is the same * as startPos when testing just for insert * @return relation of compared block against this guarded block */ public int compare(int startPos, int endPos) { try { int startThis = startMark.getOffset(); int endThis = endMark.getOffset(); if (startThis > endThis) { // swap if necessary int tmp = startThis; startThis = endThis; endThis = tmp; } int ret = 0; if (startPos == endPos) { // tested empty if (startThis == endThis) { // both empty if (startPos < startThis) { return EMPTY | THIS_EMPTY | BEFORE; } else if (startPos > startThis) { return EMPTY | THIS_EMPTY | AFTER; } else { return EMPTY | THIS_EMPTY | SAME; } } else { // tested empty, this non-empty if (startPos <= startThis) { return (startPos < startThis) ? (EMPTY | BEFORE) : (EMPTY | INSIDE_BEGIN); } else if (startPos >= endThis) { return (startPos > endThis) ? (EMPTY | AFTER) : (EMPTY | INSIDE_END); } else { return EMPTY | INNER; } } } if (startThis == endThis) { // this empty, tested non-empty if (startPos >= startThis) { return (startPos > startThis) ? (THIS_EMPTY | AFTER) : (THIS_EMPTY | EXTEND_END); } else if (endPos >= startThis) { return (endPos > startThis) ? (THIS_EMPTY | BEFORE) : (THIS_EMPTY | EXTEND_BEGIN); } else { return THIS_EMPTY | INCLUDE; } } // both non-empty if (endPos <= startThis) { return (endPos < startThis) ? BEFORE : CONTINUE_BEGIN; } else if (startPos >= endThis) { return (startPos > endThis) ? AFTER : CONTINUE_END; } else { if (endPos < endThis) { if (startPos > startThis) { return INNER; } else if (startPos == startThis) { return INSIDE_BEGIN; } else { // startPos < startThis return OVERLAP_BEGIN; } } else if (endPos == endThis) { if (startPos > startThis) { return INSIDE_END; } else if (startPos == startThis) { return SAME; } else { // startPos < startThis return EXTEND_BEGIN; } } else { // endPos > endThis if (startPos > startThis) { return OVERLAP_END; } else if (startPos == startThis) { return EXTEND_END; } else { // startPos < startThis return INCLUDE; } } } } catch (InvalidMarkException e) { return INVALID; } } public final MarkBlock getNext() { return next; } public final void setNext(MarkBlock b) { next = b; } /** Set the next block of this one in the chain. */ public void setNextChain(MarkBlock b) { this.next = b; if (b != null) { b.prev = this; } } public final MarkBlock getPrev() { return prev; } public final void setPrev(MarkBlock b) { prev = b; } /** Set the previous block of this one in the chain */ public void setPrevChain(MarkBlock b) { this.prev = b; if (b != null) { b.next = this; } } public boolean isReverse() { try { return (startMark.getOffset() > endMark.getOffset()); } catch (InvalidMarkException e) { return false; } } public void reverse() { Mark tmp = startMark; startMark = endMark; endMark = tmp; } public boolean checkReverse() { if (isReverse()) { reverse(); return true; } return false; } /** Possibly move start mark if its position is above the given number. * @return new position */ public int extendStart(int startPos) throws BadLocationException { try { int markPos = startMark.getOffset(); startPos = Math.min(startPos, markPos); if (startPos != markPos) { doc.op.moveMark(startMark, startPos); } return startPos; } catch (InvalidMarkException e) { if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N e.printStackTrace(); } return 0; } } /** Possibly move end mark if its position is above the given number. * @return new position */ public int extendEnd(int endPos) throws BadLocationException { try { int markPos = endMark.getOffset(); endPos = Math.max(endPos, markPos); if (endPos != markPos) { doc.op.moveMark(endMark, endPos); } return endPos; } catch (InvalidMarkException e) { if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N e.printStackTrace(); } return 0; } } /** Extend this mark block by start and end positions. First test whether * the given block intersects with this mark block. If not nothing is done. * @return whether this mark block has been extended */ public boolean extend(int startPos, int endPos, boolean concat) throws BadLocationException { try { boolean extended = false; int rel = compare(startPos, endPos); if ((rel & OVERLAP_BEGIN) == OVERLAP_BEGIN || ((rel & CONTINUE_BEGIN) == CONTINUE_BEGIN && concat) ) { extended = true; doc.op.moveMark(startMark, startPos); } if ((rel & OVERLAP_END) == OVERLAP_END || ((rel & CONTINUE_END) == CONTINUE_END && concat) ) { extended = true; doc.op.moveMark(endMark, endPos); } return extended; } catch (InvalidMarkException e) { if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N e.printStackTrace(); } return false; } } /** Extend this mark block by some other block. * @return whether the block was extended. If it was, the caller * is responsible for possibly removing blk from the chain */ public boolean extend(MarkBlock blk, boolean concat) { try { return extend(blk.startMark.getOffset(), blk.endMark.getOffset(), concat); } catch (BadLocationException e) { if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N e.printStackTrace(); } } catch (InvalidMarkException e) { if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N e.printStackTrace(); } } return false; } /** Shrink this mark block by the block specified. * startMark is moved to the endPos if OVERLAP_BEGIN * or INSIDE_BEGIN is returned from compare(). * endMark is moved to the startPos if OVERLAP_END * or INSIDE_END is returned from compare(). * If other status is returned or either block * is empty, then no action is taken. It's up * to caller to handle these situations. * @return relation of tested block to mark block */ public int shrink(int startPos, int endPos) throws BadLocationException { try { int rel = compare(startPos, endPos); switch (rel) { case OVERLAP_BEGIN: case INSIDE_BEGIN: doc.op.moveMark(startMark, endPos); break; case OVERLAP_END: case INSIDE_END: doc.op.moveMark(endMark, startPos); break; } return rel; } catch (InvalidMarkException e) { if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N e.printStackTrace(); } return INVALID; } } public Document getDocument() { return doc; } public int getStartOffset() { try { return startMark.getOffset(); } catch (InvalidMarkException e) { return 0; } } public int getEndOffset() { try { return endMark.getOffset(); } catch (InvalidMarkException e) { return 0; } } /** Remove the marks if they were not removed yet */ void destroyMarks() { // now remove the marks try { if (startMark != null) { startMark.remove(); startMark = null; } } catch (InvalidMarkException e) { // already removed } try { if (endMark != null) { endMark.remove(); endMark = null; } } catch (InvalidMarkException e) { // already removed } } /** Destroy the marks if necessary */ protected void finalize() throws Throwable { destroyMarks(); super.finalize(); } /** Debugs this mark block */ public String toString() { try { return "startPos=" + ((startMark != null) // NOI18N ? Utilities.debugPosition(doc, startMark.getOffset()) : "null") + ", endPos=" // NOI18N + ((endMark != null) ? Utilities.debugPosition(doc, endMark.getOffset()) : "null") // NOI18N + ", " + ((prev != null) ? ((next != null) ? "chain member" // NOI18N : "last member") : ((next != null) ? "first member" // NOI18N : "standalone member")); // NOI18N } catch (InvalidMarkException e) { return ""; // NOI18N } } /** Debug possibly whole chain of marks */ public String toStringChain() { return toString() + ((next != null) ? ("\n" + next.toStringChain()) : ""); // NOI18N } public static String debugRelation(int rel) { String s = ((rel & EMPTY) != 0) ? "EMPTY | " : ""; // NOI18N s += ((rel & THIS_EMPTY) != 0) ? "THIS_EMPTY | " : ""; // NOI18N rel &= IGNORE_EMPTY; switch (rel) { case BEFORE: return s + "BEFORE"; // NOI18N case AFTER: return s + "AFTER"; // NOI18N case CONTINUE_BEGIN: return s + "CONTINUE_BEGIN"; // NOI18N case CONTINUE_END: return s + "CONTINUE_END"; // NOI18N case OVERLAP_BEGIN: return s + "OVERLAP_BEGIN"; // NOI18N case OVERLAP_END: return s + "OVERLAP_END"; // NOI18N case EXTEND_BEGIN: return s + "EXTEND_BEGIN"; // NOI18N case EXTEND_END: return s + "EXTEND_END"; // NOI18N case INCLUDE: return s + "INCLUDE"; // NOI18N case INSIDE_BEGIN: return s + "INSIDE_BEGIN"; // NOI18N case INSIDE_END: return s + "INSIDE_END"; // NOI18N case INNER: return s + "INNER"; // NOI18N case SAME: return s + "SAME"; // NOI18N case INVALID: return s + "INVALID"; // NOI18N default: return s + "UNKNOWN_STATE " + rel; // NOI18N } } } /* * Log * 13 Gandalf-post-FCS1.11.1.0 3/8/00 Miloslav Metelka * 12 Gandalf 1.11 1/13/00 Miloslav Metelka * 11 Gandalf 1.10 1/10/00 Miloslav Metelka * 10 Gandalf 1.9 11/14/99 Miloslav Metelka * 9 Gandalf 1.8 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 8 Gandalf 1.7 10/10/99 Miloslav Metelka * 7 Gandalf 1.6 10/4/99 Miloslav Metelka * 6 Gandalf 1.5 8/17/99 Miloslav Metelka * 5 Gandalf 1.4 5/15/99 Miloslav Metelka fixes * 4 Gandalf 1.3 5/13/99 Miloslav Metelka * 3 Gandalf 1.2 4/23/99 Miloslav Metelka Undo added and internal * improvements * 2 Gandalf 1.1 4/8/99 Ian Formanek Removed debug prints * 1 Gandalf 1.0 3/18/99 Miloslav Metelka * $ */